package org.ciallo.easytokencore.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.RandomStringUtils;
import org.ciallo.easytokencore.error.EasyErrorCode;
import org.ciallo.easytokencore.exception.EasyTokenInvalidException;
import org.springframework.beans.factory.annotation.Value;

import java.security.SecureRandom;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT令牌工具类
 *
 * <p>核心功能：</p>
 * <ul>
 *   <li>生成JWT令牌</li>
 *   <li>验证JWT令牌有效性</li>
 *   <li>解析JWT令牌内容</li>
 *   <li>支持自定义payload和过期时间</li>
 * </ul>
 *
 * <p>配置参数：</p>
 * <ol>
 *   <li>SECRET: JWT签名密钥，从application配置读取</li>
 *   <li>EXPIRE_TIME: 默认过期时间(秒)</li>
 *   <li>LASTING_EXPIRE_TIME: 长时效过期时间(秒)</li>
 * </ol>
 *
 * @Description: JWT令牌工具类
 * @Author: Jukomu
 * @Package: org.ciallo.easytokencore.util
 * @Project: easy-token-demo
 * @Version: 1.0.0
 * @name: EasyTokenUtils
 * @Date: 2025/5/6
 * @Filename: EasyTokenUtils
 * @see com.auth0.jwt.JWT
 * @see com.auth0.jwt.algorithms.Algorithm
 */
public class EasyTokenUtils {
    /**
     * JWT签名密钥，从application.yml配置读取
     */
    @Value("${easy-token.secret}")
    private static String SECRET = "abc123456";

    /**
     * 默认令牌过期时间(秒)，从application.yml配置读取
     */
    @Value("${easy-token.expire}")
    private static int EXPIRE_TIME = 3600;

    /**
     * 长时效令牌过期时间(秒)，从application.yml配置读取
     */
    @Value("${easy-token.lasting-expire}")
    private static int LASTING_EXPIRE_TIME = 1296000;


    /**
     * 获取JWT创建器构建器对象
     *
     * @param expire 过期时间（秒）
     * @return JWT创建器构建器对象
     */
    public static JWTCreator.Builder getBuilder(int expire) {
        JWTCreator.Builder builder = JWT.create();
        // 发行者
        builder.withIssuer("easy-token");
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND, expire);
        builder.withExpiresAt(instance.getTime());
        // 插入框架标识
        builder.withClaim("Easy-Token", "easy-token");
        // 插入有效期
        builder.withClaim("expireTime", expire);
        return builder;
    }

    /**
     * 生成默认token,有效期 3600 秒
     *
     * @return 返回生成的token字符串
     */
    public static String getEasyToken() {
        HashMap<String, String> map = new HashMap<>();
        String random = RandomStringUtils.random(128, 0, 0, true, true, null, new SecureRandom());
        map.put("verify", random);
        return getEasyToken(map);
    }

    /**
     * 生成长有效期token,有效期 1296000 秒, 15 天
     *
     * @return 返回生成的token字符串
     */
    public static String getEasyLastingToken() {
        HashMap<String, String> map = new HashMap<>();
        String random = RandomStringUtils.random(128, 0, 0, true, true, null, new SecureRandom());
        map.put("verify", random);
        return getEasyLastingToken(map);
    }

    /**
     * 生成携带payload的token,有效期 3600 秒
     *
     * @param map 包含token信息的Map集合，其中key为字段名称，value为字段值
     * @return 返回生成的token字符串
     * @throws RuntimeException 如果在生成token过程中出现异常，则抛出运行时异常
     */
    public static String getEasyToken(Map<String, ?> map) {
        return getEasyToken(map, EXPIRE_TIME);
    }

    /**
     * 生成携带payload的token,有效期 1296000 秒, 15 天
     *
     * @param map 包含token信息的Map集合，其中key为字段名称，value为字段值
     * @return 返回生成的token字符串
     * @throws RuntimeException 如果在生成token过程中出现异常，则抛出运行时异常
     */
    public static String getEasyLastingToken(Map<String, ?> map) {
        return getEasyToken(map, LASTING_EXPIRE_TIME);
    }

    /**
     * 指定令牌过期时间,生成默认token
     *
     * @param seconds token有效期，单位为秒
     * @return 返回生成的token
     */
    public static String getEasyToken(int seconds) {
        HashMap<String, String> map = new HashMap<>();
        return getEasyToken(map, seconds);
    }

    /**
     * 指定令牌过期时间,并生成包含指定信息的JWT令牌
     *
     * @param map     一个包含用户信息的Map，其中键和值都是String类型，用于生成JWT令牌的负载部分
     * @param seconds 令牌的有效时间，单位为秒
     * @return 生成的JWT令牌字符串
     * @throws RuntimeException 如果在生成JWT令牌的过程中出现异常，将抛出运行时异常
     */
    public static String getEasyToken(Map<String, ?> map, int seconds) {
        JWTCreator.Builder builder = getBuilder(seconds);
        try {
            builder.withPayload(map);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return builder.sign(Algorithm.HMAC256(SECRET));
    }

    /**
     * 验证JWT令牌有效性
     *
     * @param token 待验证的JWT令牌字符串
     * @return 解码后的JWT对象
     * @throws EasyTokenInvalidException 当令牌无效时抛出，包含具体错误码和错误信息
     * @see EasyTokenInvalidException
     * @see EasyErrorCode
     */
    public static DecodedJWT verifyToken(String token) {
        if (VerificationUtils.isNull(token)) {
            throw new EasyTokenInvalidException(EasyErrorCode.CODE_NULL_TOKEN, "无token");
        }
        //验证令牌内容有效性 创建算法对象
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        //创建验证对象
        JWTVerifier build = JWT.require(algorithm).build();
        try {
            DecodedJWT verify = build.verify(token);
            Claim claim1 = verify.getClaim("Easy-Token");
            Claim claim2 = verify.getClaim("iss");
            if (claim1.isMissing() || claim2.isMissing()) {
                throw new EasyTokenInvalidException(EasyErrorCode.CODE_INVALID_ISSUER, "无效的发行者");
            }
            if (claim1.asString().equals("easy-token") && claim2.asString().equals("easy-token")) {
                // 验证来源
                return verify;
            }
        } catch (JWTVerificationException e) {
            if (e instanceof AlgorithmMismatchException) {
                throw new EasyTokenInvalidException(EasyErrorCode.CODE_ALGORITHM_MISMATCH_EXCEPTION, e.getMessage());
            }
            if (e instanceof SignatureVerificationException) {
                throw new EasyTokenInvalidException(EasyErrorCode.CODE_SIGNATURE_VERIFICATION_EXCEPTION, e.getMessage());
            }
            if (e instanceof TokenExpiredException) {
                throw new EasyTokenInvalidException(EasyErrorCode.CODE_TOKEN_EXPIRED_EXCEPTION, e.getMessage());
            }
            if (e instanceof InvalidClaimException) {
                throw new EasyTokenInvalidException(EasyErrorCode.CODE_INVALID_CLAIM_EXCEPTION, e.getMessage());
            }
        }
        throw new EasyTokenInvalidException(EasyErrorCode.CODE_INVALID_TOKEN, "Token校验未通过");
    }

    /**
     * 根据给定的JWT令牌获取解码后的JWT对象。
     *
     * @param token JWT令牌字符串
     * @return 解码后的JWT对象
     */
    public static DecodedJWT getDecodedJWT(String token) {
        return verifyToken(token);
    }
}
